use std::sync::Arc;

use glam::{DVec3, DVec2};

pub trait Texture {
    fn value(&self, uv: &DVec2, p: &DVec3) -> DVec3;
}

pub type TextureRef = Arc<dyn Texture>;

pub struct TexSolidColor {
    pub albedo: DVec3,
}

impl TexSolidColor {
    pub fn new(albedo: DVec3) -> TextureRef {
        Arc::new(Self { albedo })
    }
}

impl Texture for TexSolidColor {
    fn value(&self, _uv: &DVec2, _p: &DVec3) -> DVec3 {
        self.albedo
    }
}


pub struct TexChecker {
    pub inv_scale: f64,
    pub even: TextureRef,
    pub odd: TextureRef,
}

impl TexChecker {
    pub fn new(scale: f64, even: TextureRef, odd: TextureRef) -> Arc<Self> {
        Arc::new(Self { inv_scale: 1.0 / scale, even, odd })
    }

    pub fn from_color(scale: f64, even: DVec3, odd: DVec3) -> Arc<Self> {
        Self::new(
            scale, 
            TexSolidColor::new(even), 
            TexSolidColor::new(odd)
        )
    }
}

impl Texture for TexChecker {
    fn value(&self, uv: &DVec2, p: &DVec3) -> DVec3 {
        let value = (p * self.inv_scale)
            .floor()
            .element_sum() as isize;

        if value % 2 == 0 {
            self.even.value(uv, p)
        } else {
            self.odd.value(uv, p)
        }
    }
}